<?php
/**
 * IG_Model
 *
 * @author mmxie
 * @copyright Copyright (c) 2009 Green Shore (http://www.iwgame.com)
 * @version $Id: Model.php 1223 2012-01-12 08:43:50Z mmxie $
 */
abstract class IG_Model implements ArrayAccess, IteratorAggregate
{
    const DUPLICATE = 1;
    const REPLACE = 2;

    /**
     * @var IG_Mapper
     */
    protected $_mapper;

    protected $_data = array();

    protected $_modifiedFields = array();

    public function __construct($options = null)
    {
        if (empty($this->_data)) {
            $this->_data = $this->getMapper()->getEmptyData();
        }
        if (null !== $options) {
            $this->setOptions($options);
        }
    }

    /**
     *
     * @param string $name
     * @param mixed $value
     */
    public function __set($columnName, $value)
    {
        if (!array_key_exists($columnName, $this->_data)) {
            throw new IG_Exception('Specified column is not a string');
        }
        $this->_data[$columnName] = $value;
        $this->_modifiedFields[$columnName] = true;
    }

    /**
     *
     *
     * @param $name
     * @return mixed
     * @throws Exception with invalid model property
     */
    public function __get($columnName)
    {
        if (!array_key_exists($columnName, $this->_data)) {
            throw new IG_Exception("Specified column \"$columnName\" is not in the row");
        }
        return $this->_data[$columnName];
    }

    /**
     * Test existence of row field
     *
     * @param  string  $columnName   The column key.
     * @return boolean
     */
    public function __isset($columnName)
    {
        return array_key_exists($columnName, $this->_data);
    }

    public function __unset($columnName)
    {
        if (!array_key_exists($columnName, $this->_data)) {
            throw new IG_Exception("Specified column \"$columnName\" is not in the row");
        }
        unset($this->_data[$columnName]);
        return $this;
    }

    /**
     * fetch model
     *
     * - fetchKeyPair ($columns, $where, $order, $group, $limit, $offset)
     * - fetchColumn  ( ... )
     * - fetchUnique  ( ... )
     * - fetchGroup   ( ... )
     * - fetchBoth    ( ... )
     * - ......
     * @param string $method
     * @param array $params
     */
    public function __call($method, $params)
    {
        if (substr($method, 0, 5) == 'fetch') {
            return call_user_func_array(array($this->getMapper(), $method), $params);
        }

        if (substr($method, 0, 3) == 'set') {
            $name = strtolower(ltrim(preg_replace('/([A-Z]+)/', '_$1', substr($method, 3)), '_'));
            $this->__set($name, $params[0]);
            return $this;
        }

        if (substr($method, 0, 3) == 'get') {
            $name = strtolower(ltrim(preg_replace('/([A-Z]+)/', '_$1', substr($method, 3)), '_'));
            return $this->__get($name);
        }

        throw new IG_Exception("未定义方法($method)");
    }

    public function getIterator()
    {
        return new ArrayIterator((array) $this->_data);
    }

    /**
     * Proxy to __isset
     * Required by the ArrayAccess implementation
     *
     * @param string $offset
     * @return boolean
     */
    public function offsetExists($offset)
    {
        return $this->__isset($offset);
    }

    /**
     * Proxy to __get
     * Required by the ArrayAccess implementation
     *
     * @param string $offset
     * @return string
     */
     public function offsetGet($offset)
     {
         return $this->__get($offset);
     }

     /**
      * Proxy to __set
      * Required by the ArrayAccess implementation
      *
      * @param string $offset
      * @param mixed $value
      */
     public function offsetSet($offset, $value)
     {
         $this->__set($offset, $value);
     }

     /**
      * Proxy to __unset
      * Required by the ArrayAccess implementation
      *
      * @param string $offset
      */
     public function offsetUnset($offset)
     {
         return $this->__unset($offset);
     }

    /**
     * Returns the column/value data as an array.
     *
     * @return array
     */
    public function toArray()
    {
        return (array) $this->_data;
    }

    public function getModifiedFields()
    {
        return array_intersect_key($this->_data, $this->_modifiedFields);
    }

    /**
     *
     *
     * @param array $options
     * @return IG_Model
     */
    public function setOptions($data)
    {
        $data = array_intersect_key((array) $data, $this->_data);

        foreach ($data as $columnName => $value) {
            $this->__set($columnName, $value);
        }

        return $this;
    }

    /**
     * @return IG_ModelMapper|IG_Model_DbTable
     */
    public function getMapper()
    {
        if (null === $this->_mapper) {
            $mapperName = get_class($this) . 'Mapper';
            if (!class_exists($mapperName)) {
                $mapperName = 'IG_ModelMapper';
            }
            $this->setMapper(new $mapperName($this));
        }
        return $this->_mapper;
    }

    /**
     * @param IG_ModelMapper $_mapper
     * @return IG_Model
     */
    public function setMapper($_mapper)
    {
        $this->_mapper = $_mapper;
        return $this;
    }

    /**
     *
     * @return bool|int
     */
    public function save($method = null)
    {
        return $this->getMapper()->save($method);
    }

    /**
     *
     * @param $id
     * @return IG_Model|IG_Model_Rowset
     */
    public function find()
    {
        return call_user_func_array(array($this->getMapper(), 'find'), func_get_args());
    }

    /**
     * Get row's count of db-table
     *
     * @param array $where
     * @return int
     */
    public function fetchCount($where = null)
    {
        return $this->getMapper()->fetchCount($where);
    }

    /**
     * Fetches all rows.
     *
     * Honors the Zend_Db_Adapter fetch mode.
     *
     * @param string|array|Zend_Db_Table_Select $where  OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
     * @param string|array                      $order  OPTIONAL An SQL ORDER clause.
     * @param int                               $count  OPTIONAL An SQL LIMIT count.
     * @param int                               $offset OPTIONAL An SQL LIMIT offset.
     * @return Zend_Db_Table_Rowset_Abstract The row results per the Zend_Db_Adapter fetch mode.
     */
    public function fetchAll($where = null, $order = null, $limit = null, $offset = null)
    {
        return $this->getMapper()->fetchAll($where, $order, $limit, $offset);
    }

    /**
     * 分页查询
     * @param array $where
     * @param array $order
     * @param array|string $group
     * @return Zend_Paginator
     */
    public function fetchPaginator($where = null, $order = null, $group = null)
    {
        return $this->getMapper()->fetchPaginator($where, $order, $group);
    }

    /**
     * Fetches one row in an object of type Zend_Db_Table_Row_Abstract,
     * or returns null if no row matches the specified criteria.
     *
     * @param string|array|Zend_Db_Table_Select $where  OPTIONAL An SQL WHERE clause or Zend_Db_Table_Select object.
     * @param string|array                      $order  OPTIONAL An SQL ORDER clause.
     * @return Zend_Db_Table_Row_Abstract|null The row results per the
     */
    public function fetchRow($where = null, $order = null)
    {
        return $this->getMapper()->fetchRow($where, $order);
    }

    /**
     * @return int|bool The number of rows deleted.
     */
    public function delete($id = null)
    {
        return $this->getMapper()->delete($id);
    }
}